float4x4 World;
float4x4 View;
float4x4 Projection;
float4x4 InverseView;
float4x4 InverseProjection;

float4x4 LightView;
float4x4 LightProjection;
float4x4 InverseLightProjection;

float3 CameraPosition;
float3 LightPosition;
float3 LightDirection;
float3 LightColor; 

float2 HalfPixel;

float InvLightRadiusSqr;
float SpotAngle;
float SpotExponent;

bool Shadows;

texture NormalMap;
texture DepthMap;
texture ShadowMap;

sampler depthSampler = sampler_state
{
    Texture = (DepthMap);
    AddressU = CLAMP;
    AddressV = CLAMP;
    MagFilter = POINT;
    MinFilter = POINT;
    Mipfilter = POINT;
};

sampler normalSampler = sampler_state
{
    Texture = (NormalMap);
    AddressU = CLAMP;
    AddressV = CLAMP;
    MagFilter = POINT;
    MinFilter = POINT;
    Mipfilter = POINT;
};

sampler shadowSampler = sampler_state
{
    Texture = (ShadowMap);
    AddressU = CLAMP;
    AddressV = CLAMP;
    MagFilter = POINT;
    MinFilter = POINT;
    Mipfilter = POINT;
};

struct VertexShaderInput
{
    float3 Position			: POSITION0;
};

struct VertexShaderOutput
{
    float4 Position			: POSITION0;
    float4 ScreenPosition	: TEXCOORD0;
};

VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
    VertexShaderOutput output;
    float4 worldPosition = mul(float4(input.Position,1), World);
    float4 viewPosition = mul(worldPosition, View);
    output.Position = mul(viewPosition, Projection);
    output.ScreenPosition = output.Position;
    return output;
}

float3 getPosition(in float2 uv)
{
	float z = tex2D(depthSampler, uv).r;
	float x = uv.x * 2.0 - 1.0;
	float y = (1 - uv.y) * 2.0 - 1.0;
	float4 vpos = float4(x, y, z, 1.0f);
	vpos = mul(vpos, InverseProjection);
	return vpos / vpos.w;
}

float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
    //obtain screen position
    input.ScreenPosition.xy /= input.ScreenPosition.w;

    //obtain textureCoordinates corresponding to the current pixel
    //the screen coordinates are in [-1,1]*[1,-1]
    //the texture coordinates need to be in [0,1]*[0,1]
    float2 texCoord = 0.5f * (float2(input.ScreenPosition.x,-input.ScreenPosition.y) + 1);

    //allign texels to pixels
    texCoord -= HalfPixel;

    //get normal data from the normalMap
    float4 normalData = tex2D(normalSampler,texCoord);

    //tranform normal back into [-1,1] range
    float3 normal = 2.0f * normalData.xyz - 1.0f;

    //compute view-space position
	float z = tex2D(depthSampler, texCoord).r;
	float x = texCoord.x * 2.0 - 1.0;
	float y = (1 - texCoord.y) * 2.0 - 1.0;
	float4 position = float4(x, y, z, 1.0f);
	position = mul(position, InverseProjection);
	position /= position.w;

	if (Shadows)
	{
		// position in world
		float4 positionWS = mul(float4(position.xyz, 1.0), InverseView);

		// position in light space
        float4 realPositionLightVS = mul(float4(positionWS.xyz, 1.0), LightView);

        // position in light screen space
		float4 positionLightSS = mul(float4(realPositionLightVS.xyz, 1.0), LightProjection);
		positionLightSS /= positionLightSS.w;

        // sample the occlusion texture
		float2 lightTexCoord;
		lightTexCoord.x = 0.5 * (positionLightSS.x + 1);
		lightTexCoord.y = 0.5 * (-positionLightSS.y + 1);

        // unproject into the light view space
        float depthFromLight = tex2D(shadowSampler, lightTexCoord).r;
	    positionLightSS.z = depthFromLight;
	    float4 positionAsSeenByLightVS = mul(positionLightSS, InverseLightProjection);
	    positionAsSeenByLightVS /= positionAsSeenByLightVS.w;

        // clip if the position seen by the light is greater (wait what? are these values negative?)
        clip(realPositionLightVS.z - positionAsSeenByLightVS.z + 0.07);
	}

    // light position in view space
	float4 lPos = mul(float4(LightPosition, 1.0), View);

	// surface to light vector
    float3 lightVector = lPos.xyz - position.xyz;

    //compute attenuation based on distance - linear attenuation
    float attenuation = 1 - saturate(dot(lightVector, lightVector) * InvLightRadiusSqr);

    //normalize light vector
    lightVector = normalize(lightVector);

    //compute diffuse light
    float NdL = saturate(dot(normal,lightVector)) * attenuation;

    // light direction in view space
    float3 lDir = LightDirection;
    lDir = CameraPosition + LightDirection;
	lDir = normalize(mul(float4(lDir, 1.0), View));

	//spot light cone
	half spotAtten = min(1,max(0,dot(-lightVector, lDir) - SpotAngle)*SpotExponent);
	NdL *= spotAtten;
    //return saturate(dot(-lightVector, lDir) - 0.5)*SpotExponent;

	//reject pixels outside our radius or that are not facing the light
	clip(NdL -0.00001f);

    return float4(NdL * LightColor.rgb, 1.0);
}

technique Light
{
    pass Pass1
    {
        VertexShader = compile vs_2_0 VertexShaderFunction();
        PixelShader = compile ps_2_0 PixelShaderFunction();
    }
}
